﻿using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;

namespace Redis.Core
{
    public class RedisSortedSet<T> : RedisBase
    {
        public RedisSortedSet(Manager manager, String SetName)
            : base(manager.GetClient())
        {
            Name = SetName;
        }

        public RedisSortedSet(String SetName)
            : base(Manager.Default.GetClient())
        {
            Name = SetName;
        }



        public void Add(T Item)
        {
            String Value = JsonConvert.SerializeObject(Item, Formatting.None, timeConverter);
            Redis.AddItemToSortedSet(Name, Value);
        }

        public void Add(T Item, Double score)
        {
            String Value = JsonConvert.SerializeObject(Item, Formatting.None, timeConverter);
            Redis.AddItemToSortedSet(Name, Value, score);
        }



        public void Remove(T Item)
        {
            String Value = JsonConvert.SerializeObject(Item, Formatting.None, timeConverter);
            Redis.RemoveItemFromSortedSet(Name, Value);
        }
        public Int64 RemoveRange(int minRank, int maxRank)
        {
            return Redis.RemoveRangeFromSortedSet(Name, minRank, maxRank);
        }


        public Int64 RemoveRange(Int64 fromScore, Int64 toScore)
        {
            return Redis.RemoveRangeFromSortedSetByScore(Name, fromScore, toScore);
        }
        public Int64 RemoveRange(Double fromScore, Double toScore)
        {
            return Redis.RemoveRangeFromSortedSetByScore(Name, fromScore, toScore);
        }



        public T PopItemWithHighestScore()
        {
            String Value = Redis.PopItemWithHighestScoreFromSortedSet(Name);
            return JsonConvert.DeserializeObject<T>(Value);
        }

        public T PopItemWithLowestScore()
        {
            String Value = Redis.PopItemWithLowestScoreFromSortedSet(Name);
            return JsonConvert.DeserializeObject<T>(Value);
        }





        public void SetContainsItem(T Item)
        {
            String Value = JsonConvert.SerializeObject(Item, Formatting.None, timeConverter);
            Redis.SortedSetContainsItem(Name, Value);
        }



        public Int64 StoreUnion(String intoSetName, params String[] Sorteds)
        {
            if (!Sorteds.Contains(Name))
            {
                Sorteds = Sorteds.Concat(new String[] { Name }).ToArray();
            }
            return Redis.StoreUnionFromSortedSets(intoSetName, Sorteds);
        }


        public Int64 StoreIntersect(String intoSetName, params String[] Sorteds)
        {
            if (!Sorteds.Contains(Name))
            {
                Sorteds = Sorteds.Concat(new String[] { Name }).ToArray();
            }
            return Redis.StoreIntersectFromSortedSets(intoSetName, Sorteds);
        }






        public bool AddRange(string setId, IEnumerable<T> values, long score)
        {
            List<String> datas = new List<String>();
            foreach (var item in values)
            {
                datas.Add(JsonConvert.SerializeObject(item, Formatting.None, timeConverter));
            }
            return Redis.AddRangeToSortedSet(Name, datas, score);
        }

        public bool AddRange(string setId, IEnumerable<T> values, double score)
        {
            List<String> datas = new List<String>();
            foreach (var item in values)
            {
                datas.Add(JsonConvert.SerializeObject(item, Formatting.None, timeConverter));
            }
            return Redis.AddRangeToSortedSet(Name, datas, score);
        }



        public IDictionary<T, Double> GetAllWithScoresFromSortedSet(params String[] keys)
        {
            var result = new Dictionary<T, Double>();
            var list = Redis.GetAllWithScoresFromSortedSet(Name);
            foreach (var item in list)
            {
                result[JsonConvert.DeserializeObject<T>(item.Key)] = list[item.Key];
            }
            return result;
        }

        public Int64 Index(T Item)
        {
            String Value = JsonConvert.SerializeObject(Item, Formatting.None, timeConverter);
            return Redis.GetItemIndexInSortedSet(Name, Value);
        }

        public Int64 IndexDesc(T Item)
        {
            String Value = JsonConvert.SerializeObject(Item, Formatting.None, timeConverter);
            return Redis.GetItemIndexInSortedSetDesc(Name, Value);
        }

        public Double GetScore(T Item)
        {
            String Value = JsonConvert.SerializeObject(Item, Formatting.None, timeConverter);
            return Redis.GetItemScoreInSortedSet(Name, Value);
        }



        public IList<T> Items(Boolean Desc)
        {
            return Order(Desc);
        }



        public IList<T> GetRangeByHighestScore(Int64 fromScore, Int64 toScore, Int32? skip = null, Int32? take = null)
        {
            List<T> Result = new List<T>();
            List<String> Values = Redis.GetRangeFromSortedSetByHighestScore(Name, fromScore, toScore, skip, take);
            if (Values == null || Values.Count == 0)
                return Result;
            foreach (var item in Values)
            {
                Result.Add(JsonConvert.DeserializeObject<T>(item));
            }
            return Result;
        }

        public IList<T> GetRangeByHighestScore(Double fromScore, Double toScore, Int32? skip = null, Int32? take = null)
        {
            List<T> Result = new List<T>();
            List<String> Values = Redis.GetRangeFromSortedSetByHighestScore(Name, fromScore, toScore, skip, take);
            if (Values == null || Values.Count == 0)
                return Result;
            foreach (var item in Values)
            {
                Result.Add(JsonConvert.DeserializeObject<T>(item));
            }
            return Result;
        }

        public IList<T> GetRangeByLowestScore(Int64 fromScore, Int64 toScore, Int32? skip = null, Int32? take = null)
        {
            List<T> Result = new List<T>();
            List<String> Values = Redis.GetRangeFromSortedSetByLowestScore(Name, fromScore, toScore, skip, take);
            if (Values == null || Values.Count == 0)
                return Result;
            foreach (var item in Values)
            {
                Result.Add(JsonConvert.DeserializeObject<T>(item));
            }
            return Result;
        }

        public IList<T> GetRangeByLowestScore(Double fromScore, Double toScore, Int32? skip = null, Int32? take = null)
        {
            List<T> Result = new List<T>();
            List<String> Values = Redis.GetRangeFromSortedSetByLowestScore(Name, fromScore, toScore, skip, take);
            if (Values == null || Values.Count == 0)
                return Result;
            foreach (var item in Values)
            {
                Result.Add(JsonConvert.DeserializeObject<T>(item));
            }
            return Result;
        }


        public IDictionary<T,Double> GetRangeWithByHighestScore(Double fromScore, Double toScore, Int32? skip = null, Int32? take = null)
        {
            var result = new Dictionary<T, Double>();
            var list = Redis.GetRangeWithScoresFromSortedSetByHighestScore(Name, fromScore, toScore, skip,take);
            foreach (var item in list)
            {
                result[JsonConvert.DeserializeObject<T>(item.Key)] = list[item.Key];
            }
            return result;
        }


        public IDictionary<T, Double> GetRangeWithByHighestScore(Int64 fromScore, Int64 toScore, Int32? skip = null, Int32? take = null)
        {
            var result = new Dictionary<T, Double>();
            var list = Redis.GetRangeWithScoresFromSortedSetByHighestScore(Name, fromScore, toScore, skip, take);
            foreach (var item in list)
            {
                result[JsonConvert.DeserializeObject<T>(item.Key)] = list[item.Key];
            }
            return result;
        }

        public IDictionary<T, Double> GetRangeWithByLowestScore(Double fromScore, Double toScore, Int32? skip = null, Int32? take = null)
        {
            var result = new Dictionary<T, Double>();
            var list = Redis.GetRangeWithScoresFromSortedSetByLowestScore(Name, fromScore, toScore, skip, take);
            foreach (var item in list)
            {
                result[JsonConvert.DeserializeObject<T>(item.Key)] = list[item.Key];
            }
            return result;
        }

        public IDictionary<T, Double> GetRangeWithByLowestScore(Int64 fromScore, Int64 toScore, Int32? skip = null, Int32? take = null)
        {
            var result = new Dictionary<T, Double>();
            var list = Redis.GetRangeWithScoresFromSortedSetByLowestScore(Name, fromScore, toScore, skip, take);
            foreach (var item in list)
            {
                result[JsonConvert.DeserializeObject<T>(item.Key)] = list[item.Key];
            }
            return result;
        }



        public IList<T> Order(Boolean Desc)
        {
            List<T> Result = new List<T>();
            List<String> Values = new List<string>();
            if (Desc)
            {
                Values = Redis.GetAllItemsFromSortedSetDesc(Name);
            }
            else
            {
                Values = Redis.GetAllItemsFromSortedSet(Name);
            }
            if (Values == null || Values.Count == 0)
                return Result;
            foreach (var item in Values)
            {
                Result.Add(JsonConvert.DeserializeObject<T>(item));
            }
            return Result;
        }


        public Double Increment(T Item, Int64 incrementBy)
        {
            String Value = JsonConvert.SerializeObject(Item, Formatting.None, timeConverter);
            return Redis.IncrementItemInSortedSet(Name, Value, incrementBy);
        }

        public Double Decrement(T Item, Int64 incrementBy)
        {
            String Value = JsonConvert.SerializeObject(Item, Formatting.None, timeConverter);
            return Redis.IncrementItemInSortedSet(Name, Value, incrementBy);
        }

        public Double Increment(T Item, Double incrementBy)
        {
            String Value = JsonConvert.SerializeObject(Item, Formatting.None, timeConverter);
            return Redis.IncrementItemInSortedSet(Name, Value, incrementBy);
        }

        public Double Decrement(T Item, Double incrementBy)
        {
            String Value = JsonConvert.SerializeObject(Item, Formatting.None, timeConverter);
            return Redis.IncrementItemInSortedSet(Name, Value, incrementBy);
        }

 



        public Boolean ExpireAt(DateTime expireAt)
        {
            return Redis.ExpireEntryAt(Name, expireAt);
        }


        public Boolean ExpireAt(TimeSpan expireIn)
        {
            return Redis.ExpireEntryIn(Name, expireIn);
        }

        public Boolean ExpireAt(Int32 expireIn)
        {
            return Redis.ExpireEntryIn(Name, new TimeSpan(0, 0, 0, 0, expireIn));
        }


        public void Rename(String newName)
        {
            Redis.RenameKey(Name, newName);
            Name = newName;
        }


        public Int64 Count
        {
            get
            {
                return Redis.GetSortedSetCount(Name);
            }
        }




        public Boolean IsExist
        {
            get
            {
                return Redis.ContainsKey(Name);
            }
        }



        public void Delete()
        {
            Redis.Remove(Name);
        }

        public TimeSpan GetTimeToLive()
        {
            return Redis.GetTimeToLive(Name);
        }

        public String Name { get; set; }

        private static Newtonsoft.Json.Converters.IsoDateTimeConverter timeConverter = new Newtonsoft.Json.Converters.IsoDateTimeConverter()
        {
            DateTimeFormat = "yyyy-MM-dd HH:mm:ss ffff"
        };

    }
}
